package spring.chatroom.java_chatroom.common.handler;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.management.ThreadDumpEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import spring.chatroom.java_chatroom.common.config.timer.WebSocketTaskTimer;
import spring.chatroom.java_chatroom.common.datastructure.SessionQueue;
import spring.chatroom.java_chatroom.common.utils.*;
import spring.chatroom.java_chatroom.mapper.ChatInfoMapper;
import spring.chatroom.java_chatroom.pojo.WebSocketAndTime;
import spring.chatroom.java_chatroom.pojo.model.AddFriendInfo;
import spring.chatroom.java_chatroom.pojo.response.Response;
import spring.chatroom.java_chatroom.server.MesSendServer;
import spring.chatroom.java_chatroom.pojo.model.ChatInfo;
import spring.chatroom.java_chatroom.pojo.model.MsgInfo;
import spring.chatroom.java_chatroom.pojo.model.UserInfo;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

@Component
@Slf4j
public class WebSocketHandler extends TextWebSocketHandler {

    @Autowired
    MesSendServer sendServer;

    @Autowired
    ObjectMapper objectMapper;

    @Autowired
    ChatInfoMapper chatInfoMapper;

    @Getter
    @Autowired
    SessionQueue sessionQueue;

    private WebSocketTaskTimer timer;

    @Value("${socket.overtime}")
    private int socketOvertime;

    private static boolean ONCE = true;

    //毫秒
    private static final Long HOUR = 1000 * 60 * 60L;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        log.info("连接成功 {}", session.getId());

        //建立了连接之后 把userId和WebSocketSession给联系起来 这样就知道是谁的聊天了
        UserInfo user = (UserInfo) session.getAttributes().get("user");

        //因为是session过期了 所以他就会为空 但是 也有可能是ai
        if (user == null) {
            String query = session.getUri().getQuery();
            log.info("path = " + query);
            if (!"userId=0".equals(query)) {
                log.info("登录校验成功但是未储存该对话session, {}", session.getId());
                session.close(CloseStatus.NOT_ACCEPTABLE);
                return;
            }
            // 是某人上线了
            user = new UserInfo();
            user.setUserName("妹~");
            user.setUserId(0);
        }

        //如果已经登录了就给关闭了
        WebSocketSession session1 = WebSocketSessionManagerUtil.getSession(user.getUserId());

        if (session1 != null) {
            log.info("因为该连接用户已登录所以关闭旧连接");

            sendLogOffMsg(session1);

            session1.close();
        }

        //把WebSocketSession和userId绑定
        WebSocketSessionManagerUtil.setAttribute(user.getUserId(), session);

        //执行用户未执行的任务 同意好友请求
        for (Object o : WaitToSendUtil.getAttribute(user.getUserId())) {
            sendServer.tranFriendReq(user.getUserId(), (AddFriendInfo)o);
        }

        WaitToSendUtil.removeAttribute(user.getUserId());

        log.info("建立起了连接 id {}  session {}", session.getId(), session);

        if(ONCE){
            init(sessionQueue);
            ONCE = false;
        }

        updateLRU(session);
    }

    private void sendLogOffMsg(WebSocketSession session1) throws IOException {
        MsgInfo msg = new MsgInfo();
        msg.setType("log-off");
        session1.sendMessage(new TextMessage(JSONUtil.toJsonStr(Response.success(msg))));
    }

    private void init(SessionQueue sessionQueue) {
        timer = new WebSocketTaskTimer(sessionQueue);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //把最近的taskTimer储存到队列中
        updateLRU(session);

        //收到了信息之后 储存在数据库中格式为 sessionId userId content
        //解析载荷

        String messageStr = message.getPayload();

        Map<String, Object> map = objectMapper.readValue(messageStr, new TypeReference<Map<String, Object>>() {
        });

        if ("message".equals((String) map.get("type"))) {
            MsgInfo msg = objectMapper.readValue(messageStr, MsgInfo.class);

            ChatInfo c = new ChatInfo();
            BeanUtils.copyProperties(msg, c);

            //将数据储存到数据库中去
            chatInfoMapper.insertInfo(c);

            msg.setId(c.getId());

            //实现消息的转发
            sendServer.sendMsg(msg);
        } else if ("SomeBodyHello".equals(map.get("type"))) {
            //某人上线了
            WebSocketSessionManagerUtil.setAttribute(0, session);

            log.info("妹~ 你好呀 ( *´◒`*)");
            // 告诉在线的人某人上线了
            MsgInfo m = new MsgInfo();
            m.setType("message");
            m.setUserId(0);
            m.setUserName("妹~");
            m.setContent("妹妹上线咯，快来和我聊天吧 ξ( ✿＞◡❛)");

            broadcast(session, m);
        } else {
            log.error("无效的websocket 类型: {}",map.get("type"));
        }
    }

    /**
     * 更新最近使用的连接
     * @param session
     */
    private void updateLRU(WebSocketSession session) {
        if ((new Random()).nextInt() % 100 < 50) {
            return;
        }
        //更新
        log.info("向定时器中添加session {}", session);
        timer.add(new WebSocketAndTime(session, System.currentTimeMillis() + socketOvertime * HOUR));
    }

    /**
     * 向userId的所有在线朋友广播
     * @param session 排除的人
     * @param msg     广播的格式 sessionId是广播时确定的
     */
    private void broadcast(WebSocketSession session, MsgInfo msg) throws IOException, InterruptedException {
        //这时是在线的UserId
        Set<Integer> ids = WebSocketSessionManagerUtil.getMap().keySet();

        Integer sendUserId = msg.getUserId();

        //根据sendUserId 和ids id来获取到对应sessionId

        for(Integer id : ids) {//friendId
            WebSocketSession socketSession = WebSocketSessionManagerUtil.getAttribute(id);

            if(socketSession == null || socketSession == session) { continue; }

            Integer sessionId = UserIdsAndSessionId.get(getHashCode(sendUserId, id));

            if(sessionId == null) {
                log.info("sendId: {} friendId: {} 对应的会话sessionId为空", sendUserId, id);
                continue;
            }
            msg.setSessionId(sessionId);

            socketSession.sendMessage(new TextMessage(JSONUtil.toJsonStr(Response.success(msg))));
        }

    }

    private Integer getHashCode(Integer id1, Integer id2) {
        if (id1 > id2) {
            return List.of(id1, id2).hashCode();
        } else {
            return List.of(id2, id1).hashCode();
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if(WebSocketSessionManagerUtil.getAttribute(0) == session) {
            log.info("妹妹下线了");
            WebSocketSessionManagerUtil.removeAttribute(0);
            MsgInfo m = new MsgInfo();
            m.setType("message");
            m.setUserId(0);
            m.setUserName("妹妹");
            m.setContent("妹妹下播咯 ( ﾟ∀ﾟ)o彡ﾟ");
            broadcast(session, m);
            session.close();
            return;
        }
        //连接发送了错误调用 或者连接关闭
        log.error("websocket 发送了错误 sessionId: {}  msg: {}", session.getId(), exception.getMessage(), exception);
        //删除这个连接
        WebSocketSessionManagerUtil.removeAttribute(session);
        session.close();
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        UserInfo user = (UserInfo) session.getAttributes().get("user");

        if(user == null || user.getUserId() == null) {
            log.info("关闭连接发现session不存在，那么就是未登录状态: {}", session.getId());

            session.close(CloseStatus.NOT_ACCEPTABLE);

            WebSocketSessionManagerUtil.removeAttribute(session);

            return;
        }

        //会重新建立连接 刷新之后
        WebSocketSessionManagerUtil.removeAttribute(user.getUserId());
        session.close();

        //连接关闭了
        log.info("连接关闭 {}", session.getId());
    }
}


